#include "FdManager.hpp"

#include <asm-generic/socket.h>
#include <memory>
#include <mutex>
#include <shared_mutex>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/socket.h>

using namespace DBServer::coroutine;

FdContext::FdContext(int fd) 
:   is_init(false),
    is_socket(false),
    sys_nonblock(false),
    user_nonblock(false),
    is_closed(false),
    fd(fd),
    recv_timeout(-1),
    send_timeout(-1){
    init();
}

FdContext::~FdContext() {
}

void FdContext::set_timeout(int type, std::uint64_t v) {
    if (type == SO_RCVTIMEO) {
        recv_timeout = v;
    } 
    else {
        send_timeout = v;
    }
}

std::uint64_t FdContext::get_timeout(int type) {
    if (type == SO_RCVTIMEO) {
        return recv_timeout;
    } else {
        return send_timeout;
    }
}

bool FdContext::init() {
    if (is_init) {
        return true;
    }

    recv_timeout = -1;
    send_timeout = -1;

    struct stat fd_stat;
    if (fstat(fd, &fd_stat) == -1) 
    {
        is_init = false;
        is_socket = false;
    } else
    {
        is_init = true;
        is_socket = S_ISSOCK(fd_stat.st_mode);
    }

    if (is_socket) {
        int flags = fcntl(fd, F_GETFL, 0);
        if (!(flags & O_NONBLOCK))
        {
            fcntl(fd, F_SETFL, flags | O_NONBLOCK);
        }
        sys_nonblock = true;
    }
    else
    {
        sys_nonblock = false;
    }

    user_nonblock = false;
    is_closed = false;
    return is_init;
}


FdContext::ptr FdManager::get(int fd) {
    std::shared_lock<std::shared_mutex> sharing_lock(mtx);
    auto iter = datas.find(fd);
    sharing_lock.unlock();

    if (iter == datas.end())
        return nullptr;
    return iter->second;
}

void FdManager::del(int fd) {
    std::shared_lock<std::shared_mutex> sharing_lock(mtx);
    auto iter = datas.find(fd);
    sharing_lock.unlock();

    if (iter == datas.end()) {
        return;
    }
    std::unique_lock<std::shared_mutex> unique_lock(mtx);
    datas.erase(iter);
}

void FdManager::reg(int fd) {
    std::shared_lock<std::shared_mutex> sharing_lock(mtx);
    auto iter = datas.find(fd);
    sharing_lock.unlock();

    if (iter != datas.end()) {
        return;
    }

    FdContext::ptr fd_ctx = std::make_shared<FdContext>(fd);
    std::unique_lock<std::shared_mutex> unique_lock(mtx);
    datas.insert({fd, fd_ctx});
}
